package com.du.dupload;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

public class VKUploader implements Runnable {
	/**
	 * Execution flag
	 */
	private boolean executing = false;

	private String filename;

	/**
	 * Splitter cancelation flag
	 */
	private boolean canceled = false;

	private boolean finished = false;

	private boolean error = false;

	private String name = "";

	private List<String> out;

	private int progress;

	private String uploadUrl = "";

	public VKUploader(String filename) {
		super();
		this.filename = filename;
		this.name = new File(filename).getName();
		this.out = new ArrayList<String>();
	}

	private synchronized void addOut(String el) {
		out.add(el);
	}

	public synchronized void cancel() {
		this.canceled = true;
	}

	public synchronized void upload() throws IllegalStateException {
		while (isExecuting() && isCanceled()) {
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (isExecuting()) {
			return;
		}
		setExecuting(true);
		setCanceled(false);
		setFinished(false);
		setError(false);
		this.out = new ArrayList<String>();
		Thread t = new Thread(this, "Upload started");
		t.start();
	}

	private void uploadRequest() {
		URL url;
		StringBuffer buffer = new StringBuffer("");
		try {
			url = new URL("https://api.vk.com/method/video.save?name="
					+ URLEncoder.encode(getName(), "UTF-8") + "&gid="
					+ AppConfig.getInstance().getGroupId() + "&access_token="
					+ AppConfig.getInstance().getToken());
			BufferedReader br = new BufferedReader(new InputStreamReader(
					url.openStream()));
			String strTemp = "";
			while (null != (strTemp = br.readLine())) {
				buffer.append(strTemp);
			}
			JSONParser parser = new JSONParser();
			JSONObject obj = (JSONObject) parser.parse(buffer.toString());
			obj = (JSONObject) obj.get("response");
			uploadUrl = obj.get("upload_url").toString();
			addOut("https://vk.com/video" + obj.get("owner_id").toString()
					+ "_" + obj.get("vid").toString() + "?hd=2&t=2s");
			// System.out.println(uploadUrl);
		} catch (IOException | ParseException e) {
			e.printStackTrace();
			setError(true);
		}

	}

	private void startUpload() {

		String serverResponse = null;
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(300 * 1000).setConnectionRequestTimeout(300*1000).setSocketTimeout(1000*300).build();
		HttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();
		System.out.println(uploadUrl);
		HttpPost post = new HttpPost(uploadUrl);
		FileBodyProgressBar fileEntity = new FileBodyProgressBar(new File(
				filename), "video/mp4");
		MultipartEntity reqEntity = new MultipartEntity();
		reqEntity.addPart("video_file", fileEntity);
		post.setEntity(reqEntity);

		HttpResponse response;
		try {
			response = client.execute(post);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				serverResponse = EntityUtils.toString(entity);
				System.out.println(serverResponse);
				// if(serverResponse.indexOf("error")>=0){
				// setError(true);
				// }
			} else {
				setError(true);
			}
		} catch (IOException e) {
			e.printStackTrace();
			setError(true);
		}

	}

	public synchronized String getName() {
		return name;
	}

	private synchronized void setName(String name) {
		this.name = name;
	}

	public synchronized int getProgress() {
		return progress;
	}

	public synchronized List<String> getResult() {
		if (isFinished()) {
			return new ArrayList<String>(out);
		} else {
			return new ArrayList<String>();
		}
	}

	public synchronized boolean isCanceled() {
		return canceled;
	}

	public synchronized boolean isError() {
		return error;
	}

	public synchronized boolean isExecuting() {
		return executing;
	}

	public synchronized boolean isFinished() {
		return finished;
	}

	@Override
	public void run() {
		uploadRequest();
		startUpload();
		if (!isCanceled()) {
			setFinished(true);
		}
		setExecuting(false);
	}

	private synchronized void setCanceled(boolean canceled) {
		this.canceled = canceled;
	}

	private synchronized void setError(boolean error) {
		this.error = error;
		if (error == true)
			this.canceled = true;
	}

	private synchronized void setExecuting(boolean executing) {
		this.executing = executing;
	}

	private synchronized void setFinished(boolean finished) {
		this.finished = finished;
	}

	private synchronized void setProgress(int progress) {
		this.progress = progress;
	}

	private class FileBodyProgressBar extends FileBody {

		protected final File file;
		private long transferredBytes;

		public FileBodyProgressBar(final File file, final String contentType) {
			super(file);
			if (file == null) {
				throw new IllegalArgumentException("File may not be null");
			}
			this.file = file;
			// this.listener = listener;
			this.transferredBytes = 0;

		}

		public long getContentLength() {
			return this.file.length();
		}

		public InputStream getContent() throws IOException {
			return new FileInputStream(this.file);
		}

		public void writeTo(final OutputStream outstream) throws IOException {
			if (outstream == null) {
				throw new IllegalArgumentException(
						"Output stream may not be null");
			}
			InputStream instream = new FileInputStream(this.file);
			try {
				byte[] tmp = new byte[4096];
				int l;
				while ((l = instream.read(tmp)) != -1 && !isCanceled()) {
					outstream.write(tmp, 0, l);
					this.transferredBytes += l;
					setProgress((int) (((long) 100 * this.transferredBytes) / getContentLength()));
				}
				outstream.flush();

			} catch (IOException e) {
				setError(true);
			} finally {
				instream.close();
			}
		}

	}

}
